/*

  GP2X minimal library v0.A by rlyeh, (c) 2005. emulnation.info@rlyeh (swap it!)

  Thanks to Squidge, Robster, snaff, Reesy and NK, for the help & previous work! :-)

  License
  =======

  Free for non-commercial projects (it would be nice receiving a mail from you).
  Other cases, ask me first.

  GamePark Holdings is not allowed to use this library and/or use parts from it.

*/

#include "minimal.h"

unsigned long 			gp2x_dev[3];
static volatile unsigned long 	*gp2x_memregl;
volatile unsigned short 	*gp2x_memregs;
static unsigned long 		gp2x_ticks_per_second;
unsigned char 			*gp2x_screen8;
unsigned short 			*gp2x_screen15;
unsigned short 			*gp2x_logvram15[2];
unsigned long 			gp2x_physvram[2];
unsigned int			gp2x_nflip;
volatile unsigned short 	gp2x_palette[512];
int				gp2x_sound_rate=22050;
int				gp2x_sound_stereo=0;
int 				gp2x_pal_50hz=0;
int				gp2x_ram_tweaks=0;
int				gp2x_clock=200;
int 				rotate_controls=0;

unsigned char  			*gp2x_dualcore_ram;
unsigned long  			 gp2x_dualcore_ram_size;

#define MAX_SAMPLE_RATE (44100*2)

void gp2x_video_flip(void)
{
#ifdef MMUHACK
	//flushcache(gp2x_screen8,gp2x_screen8+(640*480*2),0);
#endif
  	unsigned long address=gp2x_physvram[gp2x_nflip];
	gp2x_nflip=gp2x_nflip?0:1;
  	gp2x_screen15=gp2x_logvram15[gp2x_nflip]; 
  	gp2x_screen8=(unsigned char *)gp2x_screen15; 
   	gp2x_memregs[0x290E>>1]=(unsigned short)(address & 0xFFFF);
  	gp2x_memregs[0x2910>>1]=(unsigned short)(address >> 16);
  	gp2x_memregs[0x2912>>1]=(unsigned short)(address & 0xFFFF);
  	gp2x_memregs[0x2914>>1]=(unsigned short)(address >> 16);
}


void gp2x_video_flip_single(void)
{
#ifdef MMUHACK
	//flushcache(gp2x_screen8,gp2x_screen8+(640*480*2),0);
#endif
  	unsigned long address=gp2x_physvram[0];
  	gp2x_nflip=0;
  	gp2x_screen15=gp2x_logvram15[gp2x_nflip]; 
  	gp2x_screen8=(unsigned char *)gp2x_screen15; 
  	gp2x_memregs[0x290E>>1]=(unsigned short)(address & 0xFFFF);
  	gp2x_memregs[0x2910>>1]=(unsigned short)(address >> 16);
  	gp2x_memregs[0x2912>>1]=(unsigned short)(address & 0xFFFF);
  	gp2x_memregs[0x2914>>1]=(unsigned short)(address >> 16);
}


void gp2x_video_setpalette(void)
{
  	unsigned short *g=(unsigned short *)gp2x_palette;
	unsigned short *memreg=(short unsigned int *)&gp2x_memregs[0x295A>>1];
  	int i=512;
  	gp2x_memregs[0x2958>>1]=0;                                                     
  	do { *memreg=*g++; } while(--i);
}

/*
int master_volume;
*/

unsigned long gp2x_joystick_read(int n)
{
	extern int master_volume; 
  	unsigned long res=0;
	if (n==0)
	{
	  	unsigned long value=(gp2x_memregs[0x1198>>1] & 0x00FF);
	  	if(value==0xFD) value=0xFA;
	  	if(value==0xF7) value=0xEB;
	  	if(value==0xDF) value=0xAF;
	  	if(value==0x7F) value=0xBE;
	  	res=~((gp2x_memregs[0x1184>>1] & 0xFF00) | value | (gp2x_memregs[0x1186>>1] << 16));
	
	  	/* GP2X F200 Push Button */
	  	if ((res & GP2X_UP) && (res & GP2X_DOWN) && (res & GP2X_LEFT) && (res & GP2X_RIGHT))
	  		res |= GP2X_PUSH;
	}
	
	if (num_of_joys>n)
	{
	  	/* Check USB Joypad */
		res |= gp2x_usbjoy_check(joys[n]);
	}

  	if (!rotate_controls) {
  		if ( (res & GP2X_VOL_UP) &&  (res & GP2X_VOL_DOWN)) gp2x_sound_volume(100,100);
  		if ( (res & GP2X_VOL_UP) && !(res & GP2X_VOL_DOWN)) gp2x_sound_volume(master_volume+1,master_volume+1);
  		if (!(res & GP2X_VOL_UP) &&  (res & GP2X_VOL_DOWN)) gp2x_sound_volume(master_volume-1,master_volume-1);
  	}
  	else
  	{
  		if ( (res & GP2X_VOL_UP) &&  (res & GP2X_VOL_DOWN) && (res & GP2X_START)) gp2x_sound_volume(100,100);
  		if ( (res & GP2X_VOL_UP) && !(res & GP2X_VOL_DOWN) && (res & GP2X_START)) gp2x_sound_volume(master_volume+1,master_volume+1);
  		if (!(res & GP2X_VOL_UP) &&  (res & GP2X_VOL_DOWN) && (res & GP2X_START)) gp2x_sound_volume(master_volume-1,master_volume-1);

  		if (res & GP2X_VOL_DOWN)
  		{
  			res|=GP2X_B;
  		}
  		if (res & GP2X_VOL_UP)
  		{
  			res|=GP2X_X;
  		}
  	}
  	
	return res;
}

unsigned long gp2x_joystick_press (int n)
{
	unsigned long ExKey=0;
	while(gp2x_joystick_read(n)&0x8c0ff55) { gp2x_timer_delay(150); }
	while(!(ExKey=gp2x_joystick_read(n)&0x8c0ff55)) { }
	return ExKey;
}

void gp2x_sound_volume(int l, int r)
{
	extern int master_volume; 
 	l=l<0?0:l; l=l>319?319:l; r=r<0?0:r; r=r>319?319:r;
 	if (l>0)
 		master_volume=l;
 	l=(((l*0x50)/100)<<8)|((r*0x50)/100); /*0x5A, 0x60*/
 	ioctl(gp2x_dev[2], SOUND_MIXER_WRITE_PCM, &l); /*SOUND_MIXER_WRITE_VOLUME*/
}


void gp2x_timer_delay(unsigned long ticks)
{
	unsigned long ini=gp2x_timer_read();
	while (gp2x_timer_read()-ini<ticks);
}


unsigned long gp2x_timer_read(void)
{
 	return gp2x_memregl[0x0A00>>2]/gp2x_ticks_per_second;
}

unsigned long gp2x_timer_read_real(void)
{
 	return gp2x_memregl[0x0A00>>2];
}

unsigned long gp2x_timer_read_scale(void)
{
 	return gp2x_ticks_per_second;
}

void gp2x_timer_profile(void)
{
	static unsigned long i=0;
	if (!i) i=gp2x_memregl[0x0A00>>2];
	else {
		printf("%u\n",gp2x_memregl[0x0A00>>2]-i);
		i=0;	
	}
}

static pthread_t gp2x_sound_thread=0;								// Thread for gp2x_sound_thread_play()
static volatile int gp2x_sound_thread_exit=0;							// Flag to end gp2x_sound_thread_play() thread
static volatile int gp2x_sound_buffer=0;							// Current sound buffer
static short gp2x_sound_buffers_total[(MAX_SAMPLE_RATE*16)/30];					// Sound buffer
static void *gp2x_sound_buffers[16] = {								// Sound buffers
	(void *)(gp2x_sound_buffers_total+((MAX_SAMPLE_RATE*0)/30)),
	(void *)(gp2x_sound_buffers_total+((MAX_SAMPLE_RATE*1)/30)),
	(void *)(gp2x_sound_buffers_total+((MAX_SAMPLE_RATE*2)/30)),
	(void *)(gp2x_sound_buffers_total+((MAX_SAMPLE_RATE*3)/30)),
	(void *)(gp2x_sound_buffers_total+((MAX_SAMPLE_RATE*4)/30)),
	(void *)(gp2x_sound_buffers_total+((MAX_SAMPLE_RATE*5)/30)),
	(void *)(gp2x_sound_buffers_total+((MAX_SAMPLE_RATE*6)/30)),
	(void *)(gp2x_sound_buffers_total+((MAX_SAMPLE_RATE*7)/30)),
	(void *)(gp2x_sound_buffers_total+((MAX_SAMPLE_RATE*8)/30)),
	(void *)(gp2x_sound_buffers_total+((MAX_SAMPLE_RATE*9)/30)),
	(void *)(gp2x_sound_buffers_total+((MAX_SAMPLE_RATE*10)/30)),
	(void *)(gp2x_sound_buffers_total+((MAX_SAMPLE_RATE*11)/30)),
	(void *)(gp2x_sound_buffers_total+((MAX_SAMPLE_RATE*12)/30)),
	(void *)(gp2x_sound_buffers_total+((MAX_SAMPLE_RATE*13)/30)),
	(void *)(gp2x_sound_buffers_total+((MAX_SAMPLE_RATE*14)/30)),
	(void *)(gp2x_sound_buffers_total+((MAX_SAMPLE_RATE*15)/30))
};
static volatile int sndlen=(MAX_SAMPLE_RATE*2)/60;						// Current sound buffer length

void gp2x_sound_play(void *buff, int len)
{
	int nbuff=(gp2x_sound_buffer+1)&15;							// Sound buffer to write
	memcpy(gp2x_sound_buffers[nbuff],buff,len);					// Write the sound buffer
	gp2x_sound_buffer=nbuff;								// Update the current sound buffer
	sndlen=len;										// Update the sound buffer length
}

void gp2x_sound_thread_mute(void)
{
	memset(gp2x_sound_buffers_total,0,(MAX_SAMPLE_RATE*16*2)/30);
	sndlen=(gp2x_sound_rate*2)/60;
}

static void *gp2x_sound_thread_play(void *none)
{
	int nbuff=gp2x_sound_buffer;								// Number of the sound buffer to play
	do {
		write(gp2x_dev[1], gp2x_sound_buffers[nbuff], sndlen); 				// Play the sound buffer
		ioctl(gp2x_dev[1], SOUND_PCM_SYNC, 0);						// Synchronize Audio
		nbuff=(nbuff+(nbuff!=gp2x_sound_buffer))&15;					// Update the sound buffer to play
	} while(!gp2x_sound_thread_exit);							// Until the end of the sound thread
	pthread_exit(0);
}

void gp2x_sound_thread_start(void)
{
	gp2x_sound_thread=0;
	gp2x_sound_thread_exit=0;
	gp2x_sound_buffer=0;
	gp2x_sound_set_stereo(gp2x_sound_stereo);
	gp2x_sound_set_rate(gp2x_sound_rate);
	sndlen=(gp2x_sound_rate*2)/60;
	gp2x_sound_thread_mute();
	pthread_create( &gp2x_sound_thread, NULL, gp2x_sound_thread_play, NULL);
}

void gp2x_sound_thread_stop(void)
{
	gp2x_sound_thread_exit=1;
	gp2x_timer_delay(500);
	gp2x_sound_thread=0;
	gp2x_sound_thread_mute();
}

void gp2x_sound_set_rate(int rate)
{
	ioctl(gp2x_dev[1], SNDCTL_DSP_SPEED,  &rate);
}

void gp2x_sound_set_stereo(int stereo)
{
  	ioctl(gp2x_dev[1], SNDCTL_DSP_STEREO, &stereo);
}

void gp2x_init(int ticks_per_second, int bpp, int rate, int bits, int stereo, int Hz)
{
  	gp2x_ticks_per_second=7372800/ticks_per_second; 

  	gp2x_dev[0] = open("/dev/mem",   O_RDWR); 
  	gp2x_dev[1] = open("/dev/dsp",   O_WRONLY);
  	gp2x_dev[2] = open("/dev/mixer", O_WRONLY);

       	gp2x_memregl=(unsigned long  *)mmap(0, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, gp2x_dev[0], 0xc0000000);
        gp2x_memregs=(unsigned short *)gp2x_memregl;

	gp2x_sound_volume(100,100);
       	gp2x_memregs[0x0F16>>1] = 0x830a; usleep(100000);
       	gp2x_memregs[0x0F58>>1] = 0x100c; usleep(100000); 

	/* Upper memory access */
	gp2x_dualcore_ram_size=0x2000000;
	gp2x_dualcore_ram=(unsigned char  *)mmap(0, gp2x_dualcore_ram_size, PROT_READ|PROT_WRITE, MAP_SHARED, gp2x_dev[0], 0x02000000);
	upper_malloc_init(gp2x_dualcore_ram);

	/* Take video memory */
        gp2x_physvram[0]=0x4000000-640*480*2;
        gp2x_logvram15[0]=(unsigned short *)upper_take(gp2x_physvram[0],640*480*2);
       
        gp2x_physvram[1]=0x4000000-640*480*4;
        gp2x_logvram15[1]=(unsigned short *)upper_take(gp2x_physvram[1],640*480*2);

	gp2x_screen15=gp2x_logvram15[0];
	gp2x_screen8=(unsigned char *)gp2x_screen15;	
	gp2x_nflip=0;

 	ioctl(gp2x_dev[1], SNDCTL_DSP_SETFMT, &bits);
  	ioctl(gp2x_dev[1], SNDCTL_DSP_STEREO, &stereo);
	ioctl(gp2x_dev[1], SNDCTL_DSP_SPEED,  &rate);
	//stereo = 0x80000|7; ioctl(gp2x_dev[1], SNDCTL_DSP_SETFRAGMENT, &stereo);
	//stereo = 0x00100009; ioctl(gp2x_dev[1], SNDCTL_DSP_SETFRAGMENT, &stereo);
	stereo = 0x00100007; ioctl(gp2x_dev[1], SNDCTL_DSP_SETFRAGMENT, &stereo);

	cpuctrl_init(); /* cpuctrl.cpp */
	Disable_940(); /* cpuctrl.cpp */

#ifdef MMUHACK
	/* MMU Tables Hack by Squidge */
	mmuhack(); /* squidgehack.cpp */
#endif
	/* RAM Tweaks */
	set_ram_tweaks();

  	//gp2x_memregs[0x28DA>>1]=(((bpp+1)/8)<<9)|0xAB; /*8/15/16/24bpp...*/
  	//gp2x_memregs[0x290C>>1]=320*((bpp+1)/8); /*line width in bytes*/
	gp2x_set_video_mode(bpp,320,240);

	gp2x_video_color8(0,0,0,0);
	gp2x_video_color8(255,255,255,255);
	gp2x_video_setpalette();
	
	/* USB Joysticks Initialization */
	gp2x_usbjoy_init();

	/* Set GP2X Clock */
	gp2x_set_clock(gp2x_clock);

	/* atexit(gp2x_deinit); */
}

#if defined(__cplusplus)
extern "C" {
#endif
extern int fcloseall(void);
#if defined(__cplusplus)
}
#endif

void gp2x_deinit(void)
{
  	memset(gp2x_screen15, 0, 640*480*2); gp2x_video_flip();
  	memset(gp2x_screen15, 0, 640*480*2); gp2x_video_flip();

	/* USB Joysticks Close */
	gp2x_usbjoy_close();

#ifdef MMUHACK
	//flushcache(gp2x_screen8,gp2x_screen8+(640*480*2),0);
	mmuunhack(); /* squidgehack.cpp */
#endif
	cpuctrl_deinit(); /* cpuctrl.cpp */
	gp2x_memregs[0x28DA>>1]=0x4AB; /*set video mode*/
	gp2x_memregs[0x290C>>1]=640;   
  	//munmap((void *)gp2x_dualcore_ram, gp2x_dualcore_ram_size);
  	munmap((void *)gp2x_memregl, 0x10000);
 	close(gp2x_dev[2]);
 	close(gp2x_dev[1]);
 	close(gp2x_dev[0]);
	fcloseall(); /*close all files*/
}

void gp2x_set_clock(int mhz)
{
	/* set_display_clock_div(16+8*(mhz>=220)); */ // Fixed the LCD timing issue (washed screen). Thx, god_at_hell and Aruseman.
	set_FCLK(mhz);
	set_DCLK_Div(0);
	set_920_Div(0);
}

void gp2x_set_video_mode(int bpp,int width,int height)
{
	float x, y;
	float xscale,yscale;

  	memset(gp2x_screen15, 0, 640*480*2); gp2x_video_flip();
  	memset(gp2x_screen15, 0, 640*480*2); gp2x_video_flip();
	gp2x_video_flip_single();

  	gp2x_memregs[0x28DA>>1]=(((bpp+1)/8)<<9)|0xAB; /*8/15/16/24bpp...*/
	bpp=(gp2x_memregs[0x28DA>>1]>>9)&0x3;  /* bytes per pixel */

	gp2x_pal_50hz=0;
	
	if(gp2x_memregs[0x2800>>1]&0x100) /* TV-Out ON? */
	{
		xscale=489.0; /* X-Scale with TV-Out (PAL or NTSC) */
		if (gp2x_memregs[0x2818>>1]  == 287) /* PAL? */
		{
			yscale=(width*274.0)/320.0; /* Y-Scale with PAL */
			gp2x_pal_50hz=1;
		}
		else if (gp2x_memregs[0x2818>>1]  == 239) /* NTSC? */
			yscale=(width*331.0)/320.0; /* Y-Scale with NTSC */
	}
  	else /* TV-Out OFF? */
  	{
		xscale=1024.0; /* X-Scale for LCD */
		yscale=width; /* Y-Scale for LCD */
	}

	x=(xscale*width)/320.0;
	y=(yscale*bpp*height)/240.0;
	gp2x_memregs[0x2906>>1]=(unsigned short)x; /* scale horizontal */
	gp2x_memregl[0x2908>>2]=(unsigned  long)y; /* scale vertical */
	gp2x_memregs[0x290C>>1]=width*bpp; /* Set Video Width */
}

void set_ram_tweaks(void)
{
	int args[7]={6,4,1,1,1,2,2};
	if (gp2x_ram_tweaks)
	{
		printf("RAM Tweaks Activated\n");
		if((args[0]!=-1) && (args[0]-1 < 16)) set_tRC(args[0]-1);
		if((args[1]!=-1) && (args[1]-1 < 16)) set_tRAS(args[1]-1);
		if((args[2]!=-1) && (args[2]-1 < 16)) set_tWR(args[2]-1);
		if((args[3]!=-1) && (args[3]-1 < 16)) set_tMRD(args[3]-1);
		if((args[4]!=-1) && (args[4]-1 < 16)) set_tRFC(args[4]-1);
		if((args[5]!=-1) && (args[5] < 16)) set_tRP(args[5]-1);
		if((args[6]!=-1) && (args[6]-1 < 16)) set_tRCD(args[6]-1);
	}
}

static unsigned char fontdata8x8[] =
{
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x3C,0x42,0x99,0xBD,0xBD,0x99,0x42,0x3C,0x3C,0x42,0x81,0x81,0x81,0x81,0x42,0x3C,
	0xFE,0x82,0x8A,0xD2,0xA2,0x82,0xFE,0x00,0xFE,0x82,0x82,0x82,0x82,0x82,0xFE,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x64,0x74,0x7C,0x38,0x00,0x00,
	0x80,0xC0,0xF0,0xFC,0xF0,0xC0,0x80,0x00,0x01,0x03,0x0F,0x3F,0x0F,0x03,0x01,0x00,
	0x18,0x3C,0x7E,0x18,0x7E,0x3C,0x18,0x00,0xEE,0xEE,0xEE,0xCC,0x00,0xCC,0xCC,0x00,
	0x00,0x00,0x30,0x68,0x78,0x30,0x00,0x00,0x00,0x38,0x64,0x74,0x7C,0x38,0x00,0x00,
	0x3C,0x66,0x7A,0x7A,0x7E,0x7E,0x3C,0x00,0x0E,0x3E,0x3A,0x22,0x26,0x6E,0xE4,0x40,
	0x18,0x3C,0x7E,0x3C,0x3C,0x3C,0x3C,0x00,0x3C,0x3C,0x3C,0x3C,0x7E,0x3C,0x18,0x00,
	0x08,0x7C,0x7E,0x7E,0x7C,0x08,0x00,0x00,0x10,0x3E,0x7E,0x7E,0x3E,0x10,0x00,0x00,
	0x58,0x2A,0xDC,0xC8,0xDC,0x2A,0x58,0x00,0x24,0x66,0xFF,0xFF,0x66,0x24,0x00,0x00,
	0x00,0x10,0x10,0x38,0x38,0x7C,0xFE,0x00,0xFE,0x7C,0x38,0x38,0x10,0x10,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x1C,0x1C,0x18,0x00,0x18,0x18,0x00,
	0x6C,0x6C,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x28,0x7C,0x28,0x7C,0x28,0x00,0x00,
	0x10,0x38,0x60,0x38,0x0C,0x78,0x10,0x00,0x40,0xA4,0x48,0x10,0x24,0x4A,0x04,0x00,
	0x18,0x34,0x18,0x3A,0x6C,0x66,0x3A,0x00,0x18,0x18,0x20,0x00,0x00,0x00,0x00,0x00,
	0x30,0x60,0x60,0x60,0x60,0x60,0x30,0x00,0x0C,0x06,0x06,0x06,0x06,0x06,0x0C,0x00,
	0x10,0x54,0x38,0x7C,0x38,0x54,0x10,0x00,0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00,
	0x00,0x00,0x00,0x00,0x18,0x18,0x30,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x04,0x08,0x10,0x20,0x40,0x00,0x00,
	0x38,0x4C,0xC6,0xC6,0xC6,0x64,0x38,0x00,0x18,0x38,0x18,0x18,0x18,0x18,0x7E,0x00,
	0x7C,0xC6,0x0E,0x3C,0x78,0xE0,0xFE,0x00,0x7E,0x0C,0x18,0x3C,0x06,0xC6,0x7C,0x00,
	0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x0C,0x00,0xFC,0xC0,0xFC,0x06,0x06,0xC6,0x7C,0x00,
	0x3C,0x60,0xC0,0xFC,0xC6,0xC6,0x7C,0x00,0xFE,0xC6,0x0C,0x18,0x30,0x30,0x30,0x00,
	0x78,0xC4,0xE4,0x78,0x86,0x86,0x7C,0x00,0x7C,0xC6,0xC6,0x7E,0x06,0x0C,0x78,0x00,
	0x00,0x00,0x18,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x18,0x18,0x30,
	0x1C,0x38,0x70,0xE0,0x70,0x38,0x1C,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x00,
	0x70,0x38,0x1C,0x0E,0x1C,0x38,0x70,0x00,0x7C,0xC6,0xC6,0x1C,0x18,0x00,0x18,0x00,
	0x3C,0x42,0x99,0xA1,0xA5,0x99,0x42,0x3C,0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0x00,
	0xFC,0xC6,0xC6,0xFC,0xC6,0xC6,0xFC,0x00,0x3C,0x66,0xC0,0xC0,0xC0,0x66,0x3C,0x00,
	0xF8,0xCC,0xC6,0xC6,0xC6,0xCC,0xF8,0x00,0xFE,0xC0,0xC0,0xFC,0xC0,0xC0,0xFE,0x00,
	0xFE,0xC0,0xC0,0xFC,0xC0,0xC0,0xC0,0x00,0x3E,0x60,0xC0,0xCE,0xC6,0x66,0x3E,0x00,
	0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0x00,0x7E,0x18,0x18,0x18,0x18,0x18,0x7E,0x00,
	0x06,0x06,0x06,0x06,0xC6,0xC6,0x7C,0x00,0xC6,0xCC,0xD8,0xF0,0xF8,0xDC,0xCE,0x00,
	0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00,0xC6,0xEE,0xFE,0xFE,0xD6,0xC6,0xC6,0x00,
	0xC6,0xE6,0xF6,0xFE,0xDE,0xCE,0xC6,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,
	0xFC,0xC6,0xC6,0xC6,0xFC,0xC0,0xC0,0x00,0x7C,0xC6,0xC6,0xC6,0xDE,0xCC,0x7A,0x00,
	0xFC,0xC6,0xC6,0xCE,0xF8,0xDC,0xCE,0x00,0x78,0xCC,0xC0,0x7C,0x06,0xC6,0x7C,0x00,
	0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,
	0xC6,0xC6,0xC6,0xEE,0x7C,0x38,0x10,0x00,0xC6,0xC6,0xD6,0xFE,0xFE,0xEE,0xC6,0x00,
	0xC6,0xEE,0x3C,0x38,0x7C,0xEE,0xC6,0x00,0x66,0x66,0x66,0x3C,0x18,0x18,0x18,0x00,
	0xFE,0x0E,0x1C,0x38,0x70,0xE0,0xFE,0x00,0x3C,0x30,0x30,0x30,0x30,0x30,0x3C,0x00,
	0x60,0x60,0x30,0x18,0x0C,0x06,0x06,0x00,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00,
	0x18,0x3C,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,
	0x30,0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x06,0x3E,0x66,0x66,0x3C,0x00,
	0x60,0x7C,0x66,0x66,0x66,0x66,0x7C,0x00,0x00,0x3C,0x66,0x60,0x60,0x66,0x3C,0x00,
	0x06,0x3E,0x66,0x66,0x66,0x66,0x3E,0x00,0x00,0x3C,0x66,0x66,0x7E,0x60,0x3C,0x00,
	0x1C,0x30,0x78,0x30,0x30,0x30,0x30,0x00,0x00,0x3E,0x66,0x66,0x66,0x3E,0x06,0x3C,
	0x60,0x7C,0x76,0x66,0x66,0x66,0x66,0x00,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x00,
	0x0C,0x00,0x1C,0x0C,0x0C,0x0C,0x0C,0x38,0x60,0x60,0x66,0x6C,0x78,0x6C,0x66,0x00,
	0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0xEC,0xFE,0xFE,0xFE,0xD6,0xC6,0x00,
	0x00,0x7C,0x76,0x66,0x66,0x66,0x66,0x00,0x00,0x3C,0x66,0x66,0x66,0x66,0x3C,0x00,
	0x00,0x7C,0x66,0x66,0x66,0x7C,0x60,0x60,0x00,0x3E,0x66,0x66,0x66,0x3E,0x06,0x06,
	0x00,0x7E,0x70,0x60,0x60,0x60,0x60,0x00,0x00,0x3C,0x60,0x3C,0x06,0x66,0x3C,0x00,
	0x30,0x78,0x30,0x30,0x30,0x30,0x1C,0x00,0x00,0x66,0x66,0x66,0x66,0x6E,0x3E,0x00,
	0x00,0x66,0x66,0x66,0x66,0x3C,0x18,0x00,0x00,0xC6,0xD6,0xFE,0xFE,0x7C,0x6C,0x00,
	0x00,0x66,0x3C,0x18,0x3C,0x66,0x66,0x00,0x00,0x66,0x66,0x66,0x66,0x3E,0x06,0x3C,
	0x00,0x7E,0x0C,0x18,0x30,0x60,0x7E,0x00,0x0E,0x18,0x0C,0x38,0x0C,0x18,0x0E,0x00,
	0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x00,0x70,0x18,0x30,0x1C,0x30,0x18,0x70,0x00,
	0x00,0x00,0x76,0xDC,0x00,0x00,0x00,0x00,0x10,0x28,0x10,0x54,0xAA,0x44,0x00,0x00,
};

static void gp2x_text(unsigned char *screen, int x, int y, char *text, int color)
{
	unsigned int i,l;
	screen=screen+x+y*320;

	for (i=0;i<strlen(text);i++) {
		
		for (l=0;l<8;l++) {
			screen[l*320+0]=(fontdata8x8[((text[i])*8)+l]&0x80)?color:screen[l*320+0];
			screen[l*320+1]=(fontdata8x8[((text[i])*8)+l]&0x40)?color:screen[l*320+1];
			screen[l*320+2]=(fontdata8x8[((text[i])*8)+l]&0x20)?color:screen[l*320+2];
			screen[l*320+3]=(fontdata8x8[((text[i])*8)+l]&0x10)?color:screen[l*320+3];
			screen[l*320+4]=(fontdata8x8[((text[i])*8)+l]&0x08)?color:screen[l*320+4];
			screen[l*320+5]=(fontdata8x8[((text[i])*8)+l]&0x04)?color:screen[l*320+5];
			screen[l*320+6]=(fontdata8x8[((text[i])*8)+l]&0x02)?color:screen[l*320+6];
			screen[l*320+7]=(fontdata8x8[((text[i])*8)+l]&0x01)?color:screen[l*320+7];
		}
		screen+=8;
	} 
}

void gp2x_gamelist_text_out(int x, int y, char *eltexto)
{
	char texto[33];
	strncpy(texto,eltexto,32);
	texto[32]=0;
	if (texto[0]!='-')
		gp2x_text(gp2x_screen8,x+1,y+1,texto,0);
	gp2x_text(gp2x_screen8,x,y,texto,255);
}

/* Variadic functions guide found at http://www.unixpapa.com/incnote/variadic.html */
void gp2x_gamelist_text_out_fmt(int x, int y, char* fmt, ...)
{
	char strOut[128];
	va_list marker;
	
	va_start(marker, fmt);
	vsprintf(strOut, fmt, marker);
	va_end(marker);	

	gp2x_gamelist_text_out(x, y, strOut);
}

static int log=0;

void gp2x_printf_init(void)
{
	log=0;
}

static void gp2x_text_log(char *texto)
{
	if (!log)
	{
		memset(gp2x_screen8,0,320*240);
	}
	gp2x_text(gp2x_screen8,0,log,texto,255);
	log+=8;
	if(log>239) log=0;
}

/* Variadic functions guide found at http://www.unixpapa.com/incnote/variadic.html */
void gp2x_printf(char* fmt, ...)
{
	int i,c;
	char strOut[4096];
	char str[41];
	va_list marker;
	
	va_start(marker, fmt);
	vsprintf(strOut, fmt, marker);
	va_end(marker);	

	c=0;
	for (i=0;i<strlen(strOut);i++)
	{
		str[c]=strOut[i];
		if (str[c]=='\n')
		{
			str[c]=0;
			gp2x_text_log(str);
			c=0;
		}
		else if (c==39)
		{
			str[40]=0;
			gp2x_text_log(str);
			c=0;
		}		
		else
		{
			c++;
		}
	}
}
